package com.ipan.kits.security.sm;

import java.security.Key;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;

import com.ipan.kits.base.ExceptionUtil;

/**
 * SM4加密算法
 * 
 * @author iPan
 * @date 2021-11-07
 */
public class SM4Utils {

    private static final String ENCODING = "UTF-8";
    public static final String ALGORIGTHM_NAME = "SM4";
    public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS7Padding";
    public static final int DEFAULT_KEY_SIZE = 128;

    private SM4Utils() {}

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * 生成ecb暗号
     */
    private static Cipher generateEcbCipher(String algorithmName, int mode, byte[] key) {
        Cipher cipher = null;
		try {
			cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
			Key sm4Key = new SecretKeySpec(key, ALGORIGTHM_NAME);
			cipher.init(mode, sm4Key);
		} catch (Exception e) {
			ExceptionUtil.unchecked(e);
		}
        return cipher;
    }

    /**
     * 自动生成密钥
     */
    public static byte[] generateKey() {
        return generateKey(DEFAULT_KEY_SIZE);
    }
    public static byte[] generateKey(int keySize){
    	KeyGenerator kg = null;
        try {
			kg = KeyGenerator.getInstance(ALGORIGTHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
			kg.init(keySize, new SecureRandom());
		} catch (Exception e) {
			ExceptionUtil.unchecked(e);
		}
        return kg.generateKey().getEncoded();
    }
    public static String generateKeyToHexString() {
    	return ByteUtils.toHexString(generateKey());
    }
    public static String generateKeyToHexString(int keySize) {
    	return ByteUtils.toHexString(generateKey(keySize));
    }


    /**
     * 加密
     * 返回16进制字符串密文
     */
    public static String encryptEcb(String hexKey, String paramStr) {
    	return encryptEcb(hexKey, paramStr, ENCODING);
    }
    public static String encryptEcb(String hexKey, String paramStr, String charset) {
        String cipherText = "";
        try {
			if (null != paramStr && !"".equals(paramStr)) {
			    byte[] keyData = ByteUtils.fromHexString(hexKey);
			    charset = charset.trim();
			    if (charset.length() <= 0) {
			        charset = ENCODING;
			    }
			    byte[] srcData = paramStr.getBytes(charset);
			    byte[] cipherArray = encrypt_Ecb_Padding(keyData, srcData);
			    cipherText = ByteUtils.toHexString(cipherArray);
			}
		} catch (Exception e) {
			ExceptionUtil.unchecked(e);
		}
        return cipherText;
    }

    /**
     * 加密模式之ecb
     */
    public static byte[] encrypt_Ecb_Padding(byte[] key, byte[] data) {
    	byte[] bs = null;
        try {
			Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key);
			bs = cipher.doFinal(data);
		} catch (Exception e) {
			ExceptionUtil.unchecked(e);
		}
        return bs;
    }

    /**
     * sm4解密
     * 返回原字符串
     */
    public static String decryptEcb(String hexKey, String cipherText) {
    	return decryptEcb(hexKey, cipherText, ENCODING);
    }
    public static String decryptEcb(String hexKey, String cipherText, String charset) {
        String decryptStr = "";
        try {
			byte[] keyData = ByteUtils.fromHexString(hexKey);
			byte[] cipherData = ByteUtils.fromHexString(cipherText);
			byte[] srcData = decrypt_Ecb_Padding(keyData, cipherData);
			charset = charset.trim();
			if (charset.length() <= 0) {
			    charset = ENCODING;
			}
			decryptStr = new String(srcData, charset);
		} catch (Exception e) {
			ExceptionUtil.unchecked(e);
		}
        return decryptStr;
    }

    /**
     * 解密
     */
    public static byte[] decrypt_Ecb_Padding(byte[] key, byte[] cipherText) {
    	byte[] bs = null;
    	try {
			Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key);
			bs = cipher.doFinal(cipherText);
		} catch (Exception e) {
			ExceptionUtil.unchecked(e);
		}
        return bs;
    }

    /**
     * 密码校验
     * 
     * @param hexKey 16进制字符串密钥
     * @param cipherText 16进制字符串密文
     * @param paramStr 明文
     */
    public static boolean verifyEcb(String hexKey, String cipherText, String paramStr) {
        boolean flag = false;
        try {
			byte[] keyData = ByteUtils.fromHexString(hexKey);
			byte[] cipherData = ByteUtils.fromHexString(cipherText);
			byte[] decryptData = decrypt_Ecb_Padding(keyData,cipherData);
			byte[] srcData = paramStr.getBytes(ENCODING);
			flag = Arrays.equals(decryptData,srcData);
		} catch (Exception e) {
			ExceptionUtil.unchecked(e);
		}
        return flag;
    }

    /**
     * 测试类
     */
    public static void main(String[] args) {
    	String key = SM4Utils.generateKeyToHexString();
    	System.out.println("key=" + key);
        
    	String json = "{\"name\":\"color\",\"sex\":\"man\"}";
        String encodeStr = SM4Utils.encryptEcb(key, json);
        System.out.println("密文：" + encodeStr);
        System.out.println("解密：" +SM4Utils.decryptEcb(key, encodeStr));
        System.out.println("验证：" + SM4Utils.verifyEcb(key, encodeStr, json));
    }
}
